/**
 * @author denis.onishchenko@gmail.com
 */

package com.prapp.mediag;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;

import com.feeder.FeedHandler;
import com.feeder.Feeder;
import com.feeder.SectionListModel;
import com.feeder.Text;

public final class Repository extends SectionListModel {

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("d MMMM yyyy");

    private String pakage;
    private DBHelper dbHelper;
    private Fetcher fetcher;

    private Category currentCategory;
    private Map<Long, Category> categories;
    private List ids = new ArrayList();
    private Map<Long, Integer> indexes = new HashMap<Long, Integer>();

    private int count = 0;

    private Map<Long, Post> posts = new HashMap<Long, Post>();

    private boolean cancelFetch = false;

    Repository(Context context, String pakage, String dbName, List<Category> sCategories, List<Category> categories) {
        this.pakage = pakage;
        dbHelper = new DBHelper(context, dbName);
        fetcher = new Fetcher(dbHelper);

        this.categories = new LinkedHashMap<Long, Category>();

        for (Category sCategory : sCategories)
            this.categories.put(sCategory.getId(), sCategory);

        List dbCategories = dbHelper.queryCategories();

        for (Object odbCategory : dbCategories) {
            Category dbCategory = (Category) odbCategory;

            boolean contains = false;

            for (Category category : categories) {
                if (category.getUrl().equalsIgnoreCase(dbCategory.getUrl())) {
                    contains = true;
                    break;
                }
            }

            if (!contains)
                dbHelper.deleteCategory(dbCategory);
        }

        for (Category category : categories) {
            String url = category.getUrl();

            Category dbCategory = dbHelper.queryCategoryByUrl(url);

            if (dbCategory == null)
                dbHelper.insertCategory(category);
        }

        dbCategories = dbHelper.queryCategories();
        for (Object odbCategory : dbCategories) {
            Category dbCategory = (Category) odbCategory;
            this.categories.put(dbCategory.getId(), dbCategory);
        }

        currentCategory = this.categories.get(Category.ALL_CATEGORY_ID);
    }

    public int getCount() {
        return count;
    }

    public long getId(int index) {
        return (Long) ids.get(index);
    }

    public String getSection(long id) {
        Post post = getPostById(id);

        if (post == null) return null;

        Date date = post.getDate();
        Calendar c = Calendar.getInstance();

        c.setTime(date);
        c.set(Calendar.MILLISECOND, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.HOUR, 0);

        return DATE_FORMAT.format(c.getTime());
    }

    void close() {
        fetcher.cancel();
        dbHelper.close();
    }

    Collection<Category> getCategories() {
        return categories.values();
    }

    void fetch(boolean downloadImages) {
        cancelFetch = false;

        File extDir = null;

        if (downloadImages)
            extDir = getExtDir();

        if (currentCategory.isSpecial())
            for (Iterator<Category> iterator = getCategories().iterator(); !cancelFetch && iterator.hasNext(); ) {
                Category category = iterator.next();
                fetch(category, extDir);
            }
        else
            fetch(currentCategory, extDir);

        cancelFetch = false;
    }

    void cancelFetch() {
        cancelFetch = true;
        fetcher.cancel();
    }

    void synchronize(boolean unread) {
        if (currentCategory.isAll())
            ids = unread ? dbHelper.queryPostIdsUnread() : dbHelper.queryPostIds();
        else if (currentCategory.isUnread())
            ids = dbHelper.queryPostIdsUnread();
        else if (currentCategory.isFavorite())
            ids = unread ? dbHelper.queryPostIdsUnreadFavorite() : dbHelper.queryPostIdsFavorite();
        else
            ids = unread ? dbHelper.queryPostIdsUnreadByCategory(currentCategory) : dbHelper.queryPostIdsByCategory(currentCategory);


        count = ids.size();


        indexes.clear();
        indexes = new HashMap<Long, Integer>(count);
        for (int i = 0; i < count; i++)
            indexes.put((Long) ids.get(i), i);


        posts.clear();
        posts = new HashMap<Long, Post>(count);

        onDataChanged();
    }

    Post getPostById(long id) {
        Post post = posts.get(id);

        if (post == null) {
            post = dbHelper.queryPostById(id);

            String description = post.getDescription();
            if (description != null)
                post.setDescription(Text.stripHTML(description).trim());

            posts.put(id, post);
        }

        return post;
    }

    String getDescription(long id) {
        return dbHelper.queryPostDescription(id);
    }

    Long getPreviousId(long id) {
        int i = indexes.get(id);

        if (i == 0) return null;

        id = (Long) ids.get(i - 1);
        return id;
    }

    Long getNextId(long id) {
        int i = indexes.get(id);

        if (i + 1 == getViewCount()) return null;

        id = (Long) ids.get(i + 1);
        return id;
    }

    void markAllAsRead() {
        for (int i = 0; i < getViewCount(); i++) {
            long id = (Long) ids.get(i);
            dbHelper.updatePostRead(id, true);
        }
    }

    void setCategory(Category category) {
        currentCategory = category;
    }

    long getPostCount() {
        return dbHelper.queryPostCount();
    }

    long getCategoryPostCount(long categoryId) {
        if (Category.isAll(categoryId))
            return dbHelper.queryPostCount();
        if (Category.isUnread(categoryId))
            return dbHelper.queryPostUnreadCount();
        if (Category.isFavorite(categoryId))
            return dbHelper.queryPostFavoriteCount();
        return dbHelper.queryPostCountByCategory(categoryId);
    }

    Category getCategoryById(long categoryId) {
        return categories.get(categoryId);
    }

    void updateRead(Post post) {
        dbHelper.updatePostRead(post.getId(), post.getRead());
    }

    void updateFavorite(Post post) {
        dbHelper.updatePostFavorite(post.getId(), post.getFavorite());
    }

    void deleteAllImages() {
        File extDir = getExtDir();

        if (extDir == null) return;

        File[] files = extDir.listFiles();
        if (files == null) return;

        for (File file : files)
            try {
                file.delete();
            }
            catch (Throwable x) {
            }
    }

    void deleteOldPosts() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.MONTH, c.get(Calendar.MONTH) - 1);
        dbHelper.deletePostsByDate(c.getTime());
    }

    void deleteAllPosts() {
        dbHelper.deletePosts();
    }

    private void fetch(Category category, File extDir) {
        if (category.isSpecial()) return;

        try {
            fetcher.fetch(category, extDir);
        }
        catch (Throwable x) {
            Log.e(getClass().getName() + ".fetch", "Can't fetch " + category.getUrl(), x);
        }
    }

    private File getExtDir() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state))
            return new File(Environment.getExternalStorageDirectory(), pakage + File.separator + "pictures");
        return null;
    }

    private static final class DBHelper extends SQLiteOpenHelper {

        private Category.DBAdapter categoryAdapter = new Category.DBAdapter();
        private Post.DBAdapter postAdapter = new Post.DBAdapter();

        DBHelper(Context context, String name) {
            super(context, name, null, 1);
        }

        public void onCreate(SQLiteDatabase db) {
            categoryAdapter.createTable(db);
            postAdapter.createTable(db);
        }

        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }

        void insertCategory(Category category) {
            SQLiteDatabase db = getWritableDatabase();
            categoryAdapter.insert(category, db);
        }

        List queryCategories() {
            SQLiteDatabase db = getReadableDatabase();
            return categoryAdapter.queryList(db);
        }

        Category queryCategoryByUrl(String url) {
            SQLiteDatabase db = getReadableDatabase();
            return categoryAdapter.queryByUrl(url, db);
        }

        void updateCategoryLastUpdate(long id, Date value) {
            SQLiteDatabase db = getWritableDatabase();
            categoryAdapter.updateLastUpdate(id, value, db);
        }

        void insertPost(Post post) {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.insert(post, db);
        }

        Post queryPostByLink(Category category, String link) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryByLink(category, link, db);
        }

        void deleteCategory(Category category) {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.deleteByCategory(category, db);
            categoryAdapter.delete(category, db);
        }

        void deletePostsByDate(Date date) {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.deleteByDate(date, db);
        }

        void deletePosts() {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.delete(db);
        }

        List queryPostIds() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryList(db);
        }

        List queryPostIdsUnread() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryUnreadList(db);
        }

        List queryPostIdsFavorite() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryFavoriteList(db);
        }

        List queryPostIdsUnreadFavorite() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryUnreadFavoriteList(db);
        }

        List queryPostIdsByCategory(Category category) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryByCategoryList(category, db);
        }

        List queryPostIdsUnreadByCategory(Category category) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryUnreadByCategoryList(category, db);
        }

        Post queryPostById(long id) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryById(id, db);
        }

        String queryPostDescription(long id) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryDescription(id, db);
        }

        void updatePostRead(long id, boolean value) {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.updateRead(id, value, db);
        }

        void updatePostFavorite(long id, boolean value) {
            SQLiteDatabase db = getWritableDatabase();
            postAdapter.updateFavorite(id, value, db);
        }

        long queryPostCount() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryCount(db);
        }

        long queryPostUnreadCount() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryUnreadCount(db);
        }

        long queryPostFavoriteCount() {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryFavoriteCount(db);
        }

        long queryPostCountByCategory(long categoryId) {
            SQLiteDatabase db = getReadableDatabase();
            return postAdapter.queryCountByCategory(categoryId, db);
        }
    }

    private static final class Fetcher implements FeedHandler {

        private DBHelper dbHelper;

        private Feeder feeder;
        private Category category;

        private Post post;

        protected Fetcher(DBHelper dbHelper) {
            this.dbHelper = dbHelper;

            feeder = new Feeder(this);
        }

        Category fetch(Category category, File extDir) {
            this.category = category;
            post = null;

            String categoryUrl = category.getUrl();
            feeder.parse(categoryUrl, extDir);

            Date lastUpdate = new Date();
            category.setLastUpdate(lastUpdate);
            dbHelper.updateCategoryLastUpdate(category.getId(), lastUpdate);

            this.category = null;
            post = null;


            return category;
        }

        void cancel() {
            feeder.cancel();
        }

        public void onRssChannelItemStart() {
            onPostStart();
        }

        public void onRssChannelItemEnd() {
            onPostEnd();
        }

        public void onRssChannelItemPubDate(Date value) {
            post.setDate(value);
        }

        public void onRssChannelItemAuthor(String value) {
            post.setAuthor(value);
        }

        public void onRssChannelItemDescription(String value) {
            post.setDescription(value);
        }

        public void onRssChannelItemLink(String value) {
            post.setLink(value);
        }

        public void onRssChannelItemTitle(String value) {
            post.setTitle(value);
        }

        public void onFeedEntryStart() {
            onPostStart();
        }

        public void onFeedEntryEnd() {
            onPostEnd();
        }

        public void onFeedEntryLink(String value) {
            post.setLink(value);
        }

        public void onFeedEntryUpdated(Date value) {
            post.setDate(value);
        }

        public void onFeedEntryPublished(Date value) {
            if (post.getDate() != null) {
                post.setDate(value);
            }
        }

        public void onFeedEntryAuthorName(String value) {
            if (post.getAuthor() == null) {
                post.setAuthor(value);
            }
        }

        public void onFeedEntryAuthorEmail(String value) {
            post.setAuthor(value);
        }

        public void onFeedEntrySummary(String value) {
            if (post.getDescription() != null)
                post.setDescription(value);
        }

        public void onFeedEntryContent(String value) {
            post.setDescription(value);
        }

        public void onFeedEntryTitle(String value) {
            post.setTitle(value);
        }

        private void onPostStart() {
            post = new Post();
        }

        private void onPostEnd() {
            String link = post.getLink();

            if (link != null) {
                if (post.getDate() == null)
                    post.setDate(new Date());

                Post dbPost = dbHelper.queryPostByLink(category, link);

                if (dbPost == null) {
                    if (post.getDate() == null)
                        post.setDate(new Date());

                    post.setRead(false);
                    post.setFavorite(false);
                    post.setCategoryId(category.getId());

                    dbHelper.insertPost(post);
                }
            }

            post = null;
        }
    }
}
